﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Client.Data.Services;

namespace Kocic.RepositoryManagement.Crm4
{
    /// <summary>
    /// Manages relationships for a CRM 4 instance
    /// </summary>
    /// <typeparam name="TContext">The type of the CRM data context.</typeparam>
    public static class XrmRelationshipManager<TContext>
           where TContext : CrmDataContext, new()
    {
        /// <summary>
        /// Creates a relationship between two CRM entity objects.
        /// </summary>
        /// <param name="info">An instance of RelationshipInfo containing specific relationship information.</param>
        /// <param name="context">An instance of TContext that will be used in saving relationship information. If no value is provided, this method will reference an instance of the CRM data context
        /// in an instance of the XrmContextScope class.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public static void CreateRelationship(XrmRelationshipInfo info, TContext context = null)
        {
            XrmRelationshipManager<TContext>.PerformRelationshipOperation(info, context, (inInfo, inContext) => inContext.AddLink(inInfo.Primary, inInfo.Name, info.Related));
        }

        /// <summary>
        /// Deletes a relationship between two CRM entity objects.
        /// </summary>
        /// <param name="info">An instance of RelationshipInfo containing specific relationship information.</param>
        /// <param name="context">An instance of TContext that will be used in saving relationship information. If no value is provided, this method will reference an instance of the CRM data context
        /// in an instance of the XrmContextScope class.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public static void DeleteRelationship(XrmRelationshipInfo info, TContext context = null)
        {
            XrmRelationshipManager<TContext>.PerformRelationshipOperation(info, context, (inInfo, inContext) => inContext.SetLink(inInfo.Related, inInfo.Name, null));
        }

        /// <summary>
        /// Updates a relationship between two CRM entity objects.
        /// </summary>
        /// <param name="info">An instance of RelationshipInfo containing specific relationship information.</param>
        /// <param name="context">An instance of TContext that will be used in saving relationship information. If no value is provided, this method will reference an instance of the CRM data context
        /// in an instance of the XrmContextScope class.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public static void UpdateRelationship(XrmRelationshipInfo info, TContext context = null)
        {
            XrmRelationshipManager<TContext>.PerformRelationshipOperation(info, context, (inInfo, inContext) => inContext.SetLink(inInfo.Related, inInfo.Name, inInfo.Primary));
        }

        /// <summary>
        /// Helper method in performing relationship operations.
        /// </summary>
        /// <param name="info">An instance of RelationshipInfo containing specific relationship information.</param>
        /// <param name="context">An instance of TContext that will be used in saving relationship information. If no value is provided, this method will reference an instance of the CRM data context
        /// in an instance of the XrmContextScope class.</param>
        /// <param name="contextAction">A delegate to a method that will execute the appropriate relationship operations.</param>
        private static void PerformRelationshipOperation(XrmRelationshipInfo info, TContext context, Action<XrmRelationshipInfo, TContext> contextAction)
        {
            TContext currentContext;

            if (info == null)
            {
                throw new ArgumentNullException("info", "An instance of RelationshipInfo must be provided.");
            }

            if (context == null)
            {
                if (XrmExecutionContext<TContext>.CurrentDataSource == null)
                {
                    throw new InvalidOperationException("The CRM object context must be initialized through an instance of the XrmContextScope class.");
                }

                currentContext = XrmExecutionContext<TContext>.CurrentDataSource;
            }
            else
            {
                currentContext = context;
            }

            contextAction(info, currentContext);
            currentContext.SaveChanges();
        }
    }
}